package decisionalalgorithms.guialgorithms.servicesImpl;

import decisionalalgorithms.guialgorithms.domain.MatrixBuildDetails;
import decisionalalgorithms.guialgorithms.domain.Priority;
import decisionalalgorithms.guialgorithms.repositories.AlgorithmsService;
import decisionalalgorithms.guialgorithms.repositories.MatrixRepository;
import decisionalalgorithms.guialgorithms.repositories.PriorityRepository;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;
@Service
public class AlgorithmsImpl implements AlgorithmsService {
    private MatrixRepository matrixRepository;
    private PriorityRepository priorityRepository;

    public AlgorithmsImpl(MatrixRepository matrixRepository, PriorityRepository priorityRepository) {
        this.matrixRepository = matrixRepository;
        this.priorityRepository = priorityRepository;
    }

    public Set<Priority> getPriority() {
        Set<Priority> priorities = new HashSet<>();
        priorityRepository.findAll().iterator().forEachRemaining(priorities::add);
        return priorities;
    }

    public Set<MatrixBuildDetails> getBuildDetails() {
        Set<MatrixBuildDetails> matrixBuildDetails = new HashSet<>();
        matrixRepository.findAll().iterator().forEachRemaining(matrixBuildDetails::add);
        return matrixBuildDetails;
    }

    private void buildInitMatrix(int r, int c) {
        double[][] initMatrix = new double[0][];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                initMatrix[i][j] = Math.random() * 100;
            }
        }
    }

    private static void minFromRow(double[][] matrix, int r, int c, double[] rmin) {
        for (int i = 0; i < r; i++) {
            double min = matrix[i][0];
            for (int j = 0; j < c; j++) {
                min = Math.min(min, matrix[i][j]);
            }
            rmin[i] = min;
        }
    }

    private static void minFromColumn(double[][] matrix, int r, int c, double[] cmin) {
        for (int i = 0; i < c; i++) {
            double min = matrix[0][i];
            for (int j = 0; j < r; j++) {
                min = Math.min(min, matrix[j][i]);
            }
            cmin[i] = min;
        }
    }

    private static void maxFromColumn(double[][] matrix, int r, int c, double[] cmax) {
        for (int i = 0; i < c; i++) {
            double max = matrix[0][i];
            for (int j = 0; j < r; j++) {
                max = Math.max(max, matrix[j][i]);
            }
            cmax[i] = max;
        }
    }

    private static void maxFromRow(double[][] matrix, int r, int c, double[] rmax) {
        for (int i = 0; i < r; i++) {
            double max = matrix[i][0];
            for (int j = 0; j < c; j++) {
                max = Math.max(max, matrix[i][j]);
            }
            rmax[i] = max;
        }
    }

    public static double[] calcMaxOnColumn(final double[][] array, final int r, final int c) {
        double[] maxArray = new double[c];
        double maximum = 0;
        for (int i = 0; i < c; i++) {
            maximum = array[0][i];
            for (int j = 0; j < r; j++) {
                maximum = Math.max(maximum, array[j][i]);
            }
            maxArray[i] = maximum;
        }
        return maxArray;
    }

    public static double[] calcMinOnColumn(final double[][] array, final int r, final int c) {
        double[] minArray = new double[c];
        double minimum = 0;
        for (int i = 0; i < c; i++) {
            minimum = array[0][i];
            for (int j = 0; j < r; j++) {
                minimum = Math.min(minimum, array[j][i]);
            }
            minArray[i] = minimum;
        }
        return minArray;
    }

    @Override
    public Double getOptimist(final double[][] matrix, final int r, final int c) {
        double[] rmax = new double[r];
        maxFromRow(matrix, r, c, rmax);
        double maximax = rmax[0];
        for (double v : rmax) {
            maximax = Math.max(maximax, v);
        }
        return maximax;
    }

    @Override
    public Double getPessimist(final double[][] matrix, final int r, final int c) {
        double[] rmin = new double[r];
        minFromRow(matrix, r, c, rmin);
        double minmax = rmin[0];
        for (double v : rmin) {
            minmax = Math.max(minmax, v);
        }
        return minmax;
    }

    @Override
    public Double getRealist(final double[][] matrix, final int r, final int c, final double alpha) {
        double[] rmax = new double[r];
        double[] rmin = new double[r];
        double[] result = new double[r];
        double maxR;
        maxFromRow(matrix, r, c, rmax);
        minFromRow(matrix, r, c, rmin);
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                result[i] = (alpha * rmax[i]) + ((1 - alpha) * rmin[i]);
            }
        }
        maxR = result[0];
        for (double v : result) {
            maxR = Math.max(maxR, v);
        }
        return maxR;
    }

    @Override
    public Double getRegret(final double[][] matrix, final int r, final int c) {
        double[] cmax = new double[c];
        double[][] substract = new double[r][c];
        double[] smax = new double[r];
        maxFromColumn(matrix, r, c, cmax);
        for (int i = 0; i < c; i++) {
            for (int j = 0; j < r; j++) {
                substract[j][i] = cmax[i] - matrix[j][i];
            }
        }
        maxFromRow(substract, r, c, smax);
        double min = smax[0];
        for (double v : smax) {
            min = Math.min(min, v);
        }
        return min;
    }

    @Override
    public Double getEchiprob(final double[][] matrix, final int r, final int c) {
        double[] sum = new double[r];
        double coefficient = (double) 1 / c;
        for (int i = 0; i < r; i++) {
            sum[i] = 0;
            for (int j = 0; j < c; j++) {
                sum[i] = sum[i] + matrix[i][j];
            }
            sum[i] *= coefficient;
        }
        double max = sum[0];
        for (double v : sum) {
            max = Math.max(max, v);
        }
        return max;
    }

    public static double[][] getUtility(final double[][] matrix, final int r, final int c, final String[] priority) {
        double[][] utilityMatrix;
        double[] maxValuesOnColumn = new double[c];
        double[] minValuesOnColumn = new double[c];
        maxFromColumn(matrix, r, c, maxValuesOnColumn);
        minFromColumn(matrix, r, c, minValuesOnColumn);
        utilityMatrix = calcUtilityMatrix(matrix, maxValuesOnColumn, minValuesOnColumn, priority, r, c);
        return utilityMatrix;
    }


    private static double[][] calcUtilityMatrix(final double[][] main, final double[] maxValuesOnColumn
            , final double[] minValuesOnColumn, final String[] priority, final int r, final int c) {
        double[][] utility = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority[j].equalsIgnoreCase("min")) {
                    utility[i][j] = (maxValuesOnColumn[j] - main[i][j]) / (maxValuesOnColumn[j] - minValuesOnColumn[j]);
                } else if (priority[j].equalsIgnoreCase("max")) {
                    utility[i][j] = (main[i][j] - minValuesOnColumn[j]) / (maxValuesOnColumn[j] - minValuesOnColumn[j]);
                }
            }
        }
        return utility;
    }

    public static double[][] calcGrDepartare(final double[][] mainMatrix, final String[] priority, final int r,
                                             final int c) {
        double[][] distance = new double[r][c];
        double[] maxValuesOnColumn = calcMaxOnColumn(mainMatrix, r, c);
        double[] minValuesOnColumn = calcMinOnColumn(mainMatrix, r, c);

        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority[j].equalsIgnoreCase("min")) {
                    distance[i][j] = (mainMatrix[i][j] - minValuesOnColumn[j]) / mainMatrix[i][j];
                } else if (priority[j].equalsIgnoreCase("max")) {
                    distance[i][j] = (maxValuesOnColumn[j] - mainMatrix[i][j]) / maxValuesOnColumn[j];
                }
            }
        }
        return distance;
    }


    public static double[][] calcXMatrix(final double[][] distanceMatrix, final double[] alpha, final int r,
                                         final int c) {
        double[][] xMatrix = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                xMatrix[i][j] = distanceMatrix[i][j] * alpha[j];
            }
        }
        return xMatrix;
    }

    public static double[][] calcBelogingMatrix(final double[][] xMatrix, final double exponent, final String[] priority,
                                                final int r, final int c) {
        double[][] belogingMatrix = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority[j].equalsIgnoreCase("max")) {
                    belogingMatrix[i][j] = Math.pow(exponent, xMatrix[i][j]);
                } else if (priority[j].equalsIgnoreCase("min")) {
                    belogingMatrix[i][j] = Math.pow(exponent, -xMatrix[i][j]);
                }
            }
        }
        return belogingMatrix;
    }
}
